(* e81c - For noForth c&v FR59xx vsn 200202ff, measure internal temperature
          in degrees Celcius or Fahrenheit. It is also an example of noise
          and/or outlier suppression. The median uses bubble sort which is
          also included ofcourse. The tiny median routine uses MID to get
          the median of 3 values!

    Measure & print temperature in degrees Celcius:

    VSENCE = TCSENSOR * Temp + Vsensor

    The temperature coefficient, TCSENSOR in mV/°C, represents the slope of
    the equation. VSENSOR, in mV, represents the y-intercept of the equation. 
    Temp, in °C, is the temperature of interest.

    1A1A: contains ADC 1.2-V reference Temperature sensor 30°C
    1A1C: contains ADC 1.2-V reference Temperature sensor 85°C

    Note that! The temperature is linear, but the accuracy is only +- 3%.
    You must adjust the constant CALIBRATE to your system.

    Fahrenheit, multiply by 1.8 (or 9/5) and add 32. 
    The conversion to Kelvin is simple: Kelvin = Celcius + 273.15
    All printed values are rounded to the nearest half degree integer. 

*)

v: extra definitions
decimal
290 constant CALIBRATE          \ change to calibrate individual device
                                \ changes in 10th of degrees -> 300=30.0 degrees
                                \ see SLAS704G - page 48

\ ----- temperature half degree resolution code variant
: .FIX          ( n -- )        \ Fixed point print routine, one digit behind the dot
    s>d  dup >r  dabs           \ Convert, save sign  and make number positive
    <#  #  ch . hold  #s  r> sign  #> \ Convert signed number to string 
    type  space ;               \ and show the number

hex
: >CELCIUS      ( temp -- c*10 )
    [ 1A1A @ ] literal -        \ Save it & diff. between moving average diff. and T30
    dm 550                      \ (85-30)*10
    [ 1A1C @ 1A1A @ - ] literal \ difference between T85&T30 calibration
    */                          \ calculate temp*10
    calibrate + ;               \ for single-point calibration see SLAS704G - page 48

decimal
value C?                        \  0 for Fahrenheit, 1 for Kelvin, all other Celcius
: THERMO        ( -- temp )         30 adc  1 ms ;      \ Measure MSP430 temperature once
: >FAHRENHEIT   ( temp -- f*10 )    >celcius  9 * 25 +  5 /  320 + ; \ Celsius to Fahrenheit
: >KELVIN       ( temp -- k*10 )    >celcius  2732 + ;  \ Rounded Kelvin scale
: >HALF         ( x -- y )          2 +  5 /  5 * ;     \ Round to the nearest half degree
: CR?           ( -- )              hor 75 > if  cr  then ;


(* Bare temperature printer with one digit behind the comma use:

    thermo >celcius .thermo
    thermo >kelvin .thermo

The smart version, change C? for output to a different temperature scale.
0 = Fahrenheit, 1 = Kelvin, all other numbers are Celcius

    thermo .thermometer
*)

: .THERMO       ( t*10 -- )
    base @ swap  decimal        \ Print temperature always in decimnal
    >half .fix space  base ! ;  \ Temperature in half degrees

: SCALE         ( temp -- t*10 ) \ Convert to selected temperature scale
    c? 0=  if  >fahrenheit exit  then
    c? 1 = if  >kelvin exit  then
    >celcius ;

: .THERMOMETER  ( temp -- )         cr?  scale  .thermo ; \ Smart temperature printer


hex
value THERMEM
: INIT-THERMO   ( -- )
    100 ref-on                  \ Make 1.2v ref available & use it
    2 800 **bic                 \ ADC12CTL0 - disable ADC - regs available
    8810 800 !                  \ ADC12CTL0 - set 8810=256 ADC12CLK cycs & ADC=on
    80 806 !                    \ ADC12CTL3 - temp=source - start ADCMEMn=0x0
    2 800 *bis                  \ ADC12CTL0 - enable ADC -> also locks registers
    thermo drop                 \ A dummy conversion to avoid the first outlier
    thermo to thermem ;         \ Initialise THERMEM properly ( for MOVING only )

v: forth definitions
\ *** No filtering on measurements at all
: UNFILTERED    ( -- )
    init-thermo  cr
    begin
        thermo .thermometer  80 ms
    stop? until ;



\ *** Moving average for suppressing noise
: MOVING        ( -- )
    init-thermo  cr
    begin
        thermem 7 *  thermo +  4 +  8 / \ Add 1/8 of new value and round it off
        dup to thermem  .thermometer  80 ms
    stop? until ;



\ *** Simple average measurement to middle out small peaks
: AVERAGE       ( -- )
    init-thermo  cr
    begin
        0  8 for  thermo +  next  4 +  3 rshift \ Use rounded average of 8 values
        .thermometer  80 ms
    stop? until ;



(* Jeroens median with A.N's bubble sort for suppressing outliers

Test values for bubble sort:

'thermo 9 over !
cell+  8 over !
cell+  7 over !
cell+  6 over !
cell+  5 over !
cell+ -4 over !
cell+  3 over !
cell+  2 over !
cell+  1 swap !

*)

v: extra definitions
dm 9 constant #THERMO           \ number of values used for median calculation
create 'THERMO  #thermo cells allot \ array for median of temperatures

: SAMPLE-THERMO ( -- )          \ gets #THERMO temp values in array
    #thermo for
        thermo  i 2* 'thermo + !
    next ;

: BUBBLE        ( adr #cells -- )
    for
        dup @ over                  \ ladr x1 adr
        i for                       \ ladr x1 adr )
            >r  r@ cell+ @          \ next element
            2dup < if  swap  then   \ largest smallest
            r@ !  r> cell+          \ smallest to list
        next  !                     \ last element correct
    next  drop ;

: @MEDIAN       ( -- temp )
    sample-thermo  'thermo #thermo bubble \ Fill & sort array with temperature values
    #thermo 1- 'thermo + @ ;        \ get median value


\ *** Full median good for suprressing outliers
v: forth definitions
: MEDIAN1       ( -- )
    init-thermo  cr
    begin
        @median  .thermometer  80 ms
    stop? until ;



\ A.N's tiny median on the stack for supressing regular disturbances
v: extra definitions
: 3THERMOS      ( -- x y z )        3 for  thermo  next ;
: MID           ( x y z  -- w )     2dup > if swap then  rot min max ; \ w is the median of {x,y,z}

\ *** Tiny MEDIAN short & fast but not as solid as the full median
v: forth definitions
: MEDIAN2       ( -- )
    init-thermo  cr
    begin
        3 for  3thermos mid  next  mid  \ Use median like algorithm
        .thermometer  80 ms
    stop? until ;



(* Small temperature alarm demo

    You get a message when it's to hot (25 degrees celcius or more)
    Note that although the temperature can also be printed in Fahrenheit
    or Kelvin, the code uses degrees Celcius times 10!

*)

v: extra definitions
: .ALARM    ( -- )      -1 >leds  ." Alarm! "  cr ;

\ *** Temperature alarm 
v: forth definitions
: ALARM     ( -- )      \ Give alarm when temperature goes above 24 degrees Celcius
    init-thermo  cr
    begin
        @median dup .thermometer      \ Print temperature
        >celcius  dm 240 > if  .alarm \ Flash all LEDs when high temerature
        else  40 >leds 200 ms  then   \ When not only one LED & wait
        20 ms  0 >leds 200 ms         \ LEDs off and wait
    key? until ;

shield THERMO\  freeze

\ End ;;;
